package com.simplefan.system.client.web.rest;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.simplefan.framework.client.desktop.service.ServiceFacade;
import com.simplefan.framework.common.rest.RestInnerListParameter;
import com.simplefan.framework.common.rest.RestInnerParameter;
import com.simplefan.framework.common.rest.RestObjectAnalyzerFactory;
import com.simplefan.framework.common.rest.RestParameterAnalyzer;
import com.simplefan.framework.common.rest.RestResultAnalyzer;

public class FacadeConnector {

  public String connectMethod(String restModuleName, String restMethodName, String parameter) {
    Object result = null;
    RestResultAnalyzer resultAnalyzer = RestObjectAnalyzerFactory.getRestResultAnalyzer();
    try {
      ServiceFacade facade = findFacade(restModuleName);
      RestParameterAnalyzer parameterAnalyzer = RestObjectAnalyzerFactory.getRestParameterAnalyzer(parameter);
      Class[] parameterClasses = parameterAnalyzer.getParamterTypes();
      Method method = findMethod(restModuleName, facade, restMethodName, parameterClasses);
      Object[] args = null;
      if (parameterAnalyzer.getParamters() != null && parameterAnalyzer.getParamters().size() > 0) {
        args = new Object[parameterAnalyzer.getParamters().size()];
        int i = 0;
        for (RestInnerParameter param : parameterAnalyzer.getParamters()) {
          args[i] = param.getValue();
          ++i;
        }
      }
      result = method.invoke(facade, args);
      if (result != null) {
        RestInnerParameter restInnerParameter = null;
        String className = null;
        if (result instanceof List) {
          className = List.class.getName();
          restInnerParameter = new RestInnerListParameter();
        } else {
          restInnerParameter = new RestInnerParameter();
          className = result.getClass().getName();
        }
        restInnerParameter.setTypeName(RestObjectAnalyzerFactory.getTypeNameByClassName(className));
        restInnerParameter.setValue(result);
        resultAnalyzer.setRestInnerParameter(restInnerParameter);
      }
      resultAnalyzer.setSuccessful(true);
    }
    catch (Throwable error) {
      resultAnalyzer.setSuccessful(false);
      resultAnalyzer.setErrorMessage(error.getMessage());
      resultAnalyzer.setExceptionStack(error.toString());
    }
    
    return resultAnalyzer.toXmlFragment();
  }

  private Method findMethod(String restModuleName, ServiceFacade facade, String restMethodName, Class[] parameterClasses) {
    if (restMethodName != null) {
        Method method = null;
        try {
          method = facade.getClass().getMethod(restMethodName, parameterClasses);
        }
        catch(Exception error) {
          throw new FacadeMethodNotFoundException("can not find method " + restMethodName);
        }
        return method;
    }
    else {
      throw new FacadeMethodNotFoundException("can not find method " + restMethodName);
    }
  }

  private ServiceFacade findFacade(String restModuleName) {
    if (restModuleName != null) {
      if (facades == null) {
        facades = new HashMap<String, ServiceFacade>();
      }
      if (facades.get(restModuleName) == null) {
        try {
          Class glass = findClientContextClass(restModuleName);
          Method method = glass.getMethod("getInstance");
          Object context = method.invoke(null);
          ServiceFacade facade = (ServiceFacade) glass.getMethod("getServiceFacade").invoke(context);
          facades.put(restModuleName, facade);
        }
        catch(Exception error) {
           throw new FacadeNotFoundException("Can not find facade for module " + restModuleName, error);
        }
      }
      
      return facades.get(restModuleName);
    }
    else {
      throw new FacadeNotFoundException("Can not find facade for module " + restModuleName);
    }
  }

  private Class findClientContextClassByTotalName(String className) {
    try {
        Class glass = Class.forName(className);
        return glass;
    }
    catch (ClassNotFoundException exception) {
      return null;
    }
  }
  
  private Class findClientContextClass(String restModuleName) throws ClassNotFoundException {
    Class glass = findClientContextClassByTotalName("com.simplefan.application.business.basic." + restModuleName.toLowerCase() + ".client.desktop." + restModuleName+ "ClientContext");
    if (glass == null) {
      glass = findClientContextClassByTotalName("com.simplefan.application." + restModuleName.toLowerCase() + ".client.desktop." + restModuleName+ "ClientContext");
    }
    if (glass == null) {
      glass = findClientContextClassByTotalName("com.simplefan.application.business." + restModuleName.toLowerCase() + ".client.desktop." + restModuleName+ "ClientContext");
    }
    if (glass == null) {
      glass = findClientContextClassByTotalName("com.simplefan.application.basic." + restModuleName.toLowerCase() + ".client.desktop." + restModuleName+ "ClientContext");
    }
    if (glass == null) {
      throw new ClassNotFoundException(restModuleName+ "ClientContext");
    }

    return glass;
  }

  
  @SuppressWarnings("unchecked")
  private Map<String, ServiceFacade> facades;
}
